package svcnode.worker;

import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.ArrayList;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.nio.channels.ServerSocketChannel;

import net.sf.json.JSONObject;
import net.sf.json.JSONArray;

import soar.basic.*;

import svcnode.share.*;
import svcnode.worker.session.Session;

public class Worker extends Thread
{
    private static final String __CLASS__=Worker.class.getName();

    private static final int CONN_IDLE_TIMEOUT=120;

    private int idx;
    private Ep ep;

    public Worker(int worker_idx, ServerSocketChannel server_channel)
    {/*{{{*/
        this.idx=worker_idx;
        this.ep=new Ep();
        this.ep.addListen(server_channel);
    }/*}}}*/

    public void run()
    {/*{{{*/
        final String __METHOD__="run";
        Operate operate=Operate.getInstance();
        final int UPDATE_STAT_INTERVAL=10*1000;
        final int CHECK_OPERATE_INTERVAL=1*1000;
        long last_stat=0;
        long last_operate=0;

        while(true)
        {
            ArrayList<Ep.ActiveSlot> list=this.ep.poll();
            for(Ep.ActiveSlot slot : list)
            {
                Errlog.add("%s.%s: worker(%d) slot.type(%d)",
                        __CLASS__, __METHOD__, this.idx, slot.type);
                switch(slot.type)
                {
                    case CONN:
                        conn(slot);
                        break;
                    case RECV:
                        recv(slot);
                        break;
                    case ABORT:
                        abort(slot);
                        break;
                    case TIMEOUT:
                        timeout(slot);
                        break;
                }
            }

            long now=System.currentTimeMillis();

            // 更新统计
            if(now - last_stat > UPDATE_STAT_INTERVAL)
            {
                last_stat=now;
            }

            // 判断是否退出
            if(now - last_operate > CHECK_OPERATE_INTERVAL)
            {
                if(operate.canShutdown())
                {
                    operate.WorkerStop(this.idx);
                    Errlog.add("%s.%s: worker(%d) stop",
                            __CLASS__, __METHOD__, this.idx);
                    break;
                }
                last_operate=now;
            }
        }
    }/*}}}*/

    private void conn(Ep.ActiveSlot slot)
    {/*{{{*/
        int new_slot_id=this.ep.add(slot.conn.channel);
        if(new_slot_id < 0)
        {
            Errlog.add(__CLASS__+": add new conn fail, error:%d",
                    new_slot_id);
            try
            {
                slot.conn.channel.close();
            }
            catch(Exception e)
            {
            }
            return;
        }
        Errlog.add(__CLASS__+": new conn(%d)", new_slot_id);
        this.ep.setTimeout(new_slot_id, CONN_IDLE_TIMEOUT);
        this.ep.setWrap(new_slot_id);

        //System.out.println("conn.addr: "+slot.conn.addr);
        Stat stat=Stat.getInstance();
        stat.clientConn(this.idx, new_slot_id, slot.conn.addr);
    }/*}}}*/

    private void recv(Ep.ActiveSlot slot)
    {/*{{{*/
        Errlog.add(__CLASS__+": worker(%d) slot(%d) recv",
                this.idx, slot.slot_id);
        String response=callsvc(new String(slot.recv.msg));
        boolean res=this.ep.send(slot.slot_id, response.getBytes());
        if(res == false)
        {
            Errlog.add(__CLASS__+": worker(%d) slot(%d) send fail",
                    this.idx, slot.slot_id);
        }
    }/*}}}*/

    private void abort(Ep.ActiveSlot slot)
    {/*{{{*/
        Stat stat=Stat.getInstance();
        stat.clientDisconn(this.idx, slot.slot_id);
        Errlog.add(__CLASS__+": client %d abort", slot.slot_id);
    }/*}}}*/

    private void timeout(Ep.ActiveSlot slot)
    {/*{{{*/
        Stat stat=Stat.getInstance();
        stat.clientDisconn(this.idx, slot.slot_id);
        Errlog.add(__CLASS__+": client %d timeout", slot.slot_id);
    }/*}}}*/

    private String callsvc(String request_s)
    {/*{{{*/
        final String __METHOD__="callsvc";
        Cnfg cnfg=Cnfg.getInstance();

        String response=null;

        try
        {
            JSONObject request=JSONObject.fromObject(request_s);
            if(request.has("node"))
            {
                String node=request.getString("node").toUpperCase();
                String svc=request.getString("svc").toUpperCase();
                JSONObject paras=request.getJSONObject("paras");
                if(node.equals("SESSION"))
                {
                    if(cnfg.session.enable)
                    {
                        response=Session.getInstance().call(svc, paras);
                    }
                    else
                    {
                        Errlog.add("%s.%s: node(%s) not enabled",
                                __CLASS__, __METHOD__, node);
                        response="{\"errcode\":\""+SOAR_ERR.SVC_NOT_FOUND+
                            "\"}";
                    }
                }
                else
                {
                    response="{\"errcode\":\""+SOAR_ERR.SVC_NOT_FOUND+"\"}";
                }
            }
            else
            {
                int len=request_s.length();
                if(len > 100) len=100;
                Errlog.add("%s.%s: request(%s) node not found",
                        __CLASS__, __METHOD__,
                        request_s.substring(0, len-1));
                response="{\"errcode\":\""+SOAR_ERR.SVC_NOT_FOUND+"\"}";
            }
        }
        catch(Exception e)
        {
            int len=request_s.length();
            if(len > 100) len=100;
            Errlog.add("%s.%s: parse request(%s) fail",
                   __CLASS__, __METHOD__, request_s.substring(0, len-1));
            return "{\"errcode\":\""+SOAR_ERR.MSG+"\"}";
        }

        return response; 
    }/*}}}*/
}
